﻿//-------------------------------------------------------------------------------------
// <copyright file="GetFiles.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation. All rights reserved.
//      THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 
//      EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
//      WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
// </copyright>
// <author>Mark Phillips</author>
// <email>v-mphill@microsoft.com</email>
// <date>2004-08-19</date>
// <summary>
//      Allows an item list to be produced based upon searching a directory for matching file types.
//      Multiple file types to search can be specified. Also, multiple exclusions can be specified
//      that can contain folders and explicit files.
// </summary>  
//-------------------------------------------------------------------------------------


namespace Microsoft.Sdc.Tasks.File
{
  #region Using directives

  using System;
  using System.Collections.Generic;
  using System.Collections.Specialized;
  using System.IO;
  using System.Text;

  using Microsoft.Build.Framework;
  using Microsoft.Build.Utilities;

  #endregion

  #region Class Comments
  /// <summary>
  ///      Allows an item list to be produced based upon searching a directory for matching file types.
  ///      Multiple file types to search can be specified.
  /// 
  ///      Multiple inclusions and exclusions can be specified, both of which can contain folders and 
  ///      explicit files.
  /// </summary>
  /// <remarks>
  /// <code><![CDATA[<File.GetFiles Path="path" Masks="masks" Include="include" Exclude="exclude" Recursive="recursive" Group="group">
  ///                 <Output TaskParameter="OutputFiles" ItemName="OutputFiles" />
  ///                </File.GetFiles>]]></code>
  /// <para>where:</para>
  /// <para><i>path (Required)</i></para>
  /// <para>
  /// An item list of paths to use as the root for file searches.
  /// </para>
  /// <para><i>include (Optional)</i></para>
  /// <para>
  /// An item list of files to include.  This can be usefull for adding files that 
  /// don't physically exist in the directory being searched.
  /// </para>
  /// <para><i>exclude (Optional)</i></para>
  /// <para>
  /// An item list of folders and/or absolute files that should be excluded from the search.
  /// </para>
  /// <para><i>recursive (Optional)</i></para>
  /// <para>
  /// Specifies if the search should include all sub-folders under the paths specified by the <i>Path</i> property.
  /// This defaults to true.
  /// </para>
  /// <para><i>group (Optional)</i></para>
  /// <para>
  /// Specifies the value to apply to an attribute of the item list produced by the task.  The attribute is called Group.
  /// </para>
  /// <para><i>OutputFiles (Output)</i></para>
  /// <para>
  /// The item list resulting from the file search.
  /// </para>
  /// </remarks>
  /// <example>
  /// <code><![CDATA[
  /// <Project>
  ///     <Target Name="Test">
  ///         <File.GetFiles 
  ///             Path="c:\MyXmlFiles"
  ///             Masks="*.xml">
  ///           <Output TaskParameter="OutputFile" ItemName="MyXmlFilesItemList"/>
  ///         </File.GetFiles>
  ///     </Target>
  /// </Project>
  /// ]]></code>    
  /// </example>
  #endregion
  public class GetFiles : TaskBase
  {
#region Private Variables

    private string[] path = null;
    private bool recursive = true;
    private string[] masks = null;
    private string[] inclusions = null;
    private string[] exclusions = null;
    private ITaskItem[] outputFiles = null;
    private string groupName = String.Empty;

#endregion

#region Constructors

    public GetFiles()
    {
    }

#endregion

#region Public Properties



    public bool Recursive
    {
      get
      {
        return recursive;
      }

      set
      {
        recursive = value;
      }
    }

    public string Group
    {
      get
      {
        return groupName;
      }
      set
      {
        groupName = value;
      }
    }

    [Required]
    public string[] Path
    {
      get
      {
        return path;
      }

      set
      {
        path = value;
      }
    }

    [Required]
    public string[] Masks
    {
      get
      {
        return masks;
      }

      set
      {
        masks = value;
      }
    }

    public string[] Include
    {
      get
      {
        return this.inclusions;
      }
      set
      {
        this.inclusions = value;
      }
    }

    public string[] Exclude
    {
      get
      {
        return exclusions;
      }

      set
      {
        exclusions = value;
      }
    }

    [Output]
    public ITaskItem[] OutputFiles
    {
      get
      {
        return outputFiles;
      }

      set
      {
        outputFiles = value;
      }
    }

   
#endregion

#region Public Members

    protected override void InternalExecute()
    {
      if (this.masks == null || this.masks.Length == 0)
      {
        throw new Exception("Must specify at least one mask.");
      }

      if (this.path == null || this.path.Length == 0)
      {
        throw new ArgumentException("Property Path must contain at least one valid directory");
      }

      StringCollection directories = new StringCollection();
      StringCollection files = new StringCollection();

      foreach (string folder in path)
      {
        GetDirectories(folder, ref directories, this.recursive);
      }

      if (directories.Count > 0)
      {
        foreach(string dir in directories)
        {
          foreach (string mask in this.masks)
          {
            string[] fileList = Directory.GetFiles(dir, mask);
            if (fileList.Length > 0)
            {
              foreach (string file in fileList)
              {
                if (!IsExcludedFile(file))
                {
                  files.Add(file);
                }
              }
            }
          }
        }
      }

      if (this.inclusions != null && this.inclusions.Length > 0)
      {
        foreach (string includeFile in this.inclusions)
        {
          if (!FileExists(files, includeFile))
          {
            files.Add(includeFile);
          }
        }
      }

      this.outputFiles = new ITaskItem[files.Count];
      int index = 0;
      foreach (string file in files)
      {
        ITaskItem taskItem = new TaskItem(file);
        if (this.groupName != null && this.groupName != String.Empty && this.groupName.Length > 0)
        {
          taskItem.SetMetadata("Group", this.groupName);
        }
        this.outputFiles[index] = taskItem;
        index++;
      }
    }

#endregion

#region Private Members

    private bool FileExists(StringCollection col, string matchFile)
    {
      if (col != null && col.Count > 0)
      {
        foreach (string file in col)
        {
          if (System.IO.Path.GetFullPath(matchFile).ToUpper() == System.IO.Path.GetFullPath(file).ToUpper())
          {
            return true;
          }
        }
      }

      return false;
    }

    private bool IsExcludedPath(string path)
    {
      if (Directory.Exists(path) && this.exclusions != null && this.exclusions.Length > 0)
      {
        foreach (string matchPath in this.exclusions)
        {
          if (System.IO.Path.GetFullPath(path).ToUpper() == System.IO.Path.GetFullPath(matchPath).ToUpper())
          {
            return true;
          }
        }
      }

      return false;
    }

    private bool IsExcludedFile(string filename)
    {
      if (System.IO.File.Exists(filename) && this.exclusions != null && this.exclusions.Length > 0)
      {
        foreach (string matchFile in this.exclusions)
        {
          if (System.IO.Path.GetFullPath(filename).ToUpper() == System.IO.Path.GetFullPath(matchFile).ToUpper())
          {
            return true;
          }
        }
      }

      return false;
    }

    private void GetDirectories(string path, ref StringCollection dirs, bool recursive)
    {
      if (!IsExcludedPath(path))
      {
        dirs.Add(path);

        string[] directories = Directory.GetDirectories(path);

        if (directories.Length > 0)
        {
          foreach (string dir in directories)
          {
            if (recursive)
            {
              GetDirectories(dir, ref dirs, recursive);
            }
          }
        }
      }
    }

#endregion
  }
}
